<?php

namespace tbollmeier\kakuromaster\core;

use tbollmeier\combinatorix\Permutations;

class Group
{
    
    public static function classInit()
    {
        Group::$sums = new Sums([1, 2, 3, 4, 5, 6, 7, 8, 9]);
    }

    public static function getSums()
    {
        return self::$sums;
    }
    
    public function __construct($id, $sum)
    {
        $this->id = $id;
        $this->sum = $sum;
        $this->cells = [];
        $this->validSummands = null;
    }
    
    public function addCell($cell, $mutual=true)
    {
        array_push($this->cells, $cell);
        if ($mutual) {
            $cell->addGroup($this, false);
        }
    }
    
    public function adjust()
    {
        $affectedGroups = [];
        
        $newValuesArr = $this->getAllowedCellValues();
        
        $numCells = count($this->cells);
        for ($i=0; $i<$numCells; $i++) {
            $cell = $this->cells[$i];
            $newValues = $newValuesArr[$i];
            if ($this->contentDiffers($cell, $newValues)) {
                $cell->values = $newValues;
                $affectedGroups = array_merge($affectedGroups, $cell->getOtherGroups($this));
            }
        }
        
        return $affectedGroups;
    }
    
    public function getId()
    {
        return $this->id;
    }
    
    public function getCells()
    {
        return $this->cells;
    }
    
    public function getSum()
    {
        return $this->sum;
    }
    
    public function getSize()
    {
        return count($this->cells);
    }
    
    private static $sums;
    private $id;
    private $sum;
    private $cells;
    private $validSummands;
    
    private function contentDiffers($cell, $newValues)
    {
        $oldValues = $cell->values;
        $oldSize = count($oldValues);
        if ($oldSize != count($newValues)) {
            return true;
        }
        
        sort($oldValues);
        sort($newValues);
        
        for ($i=0; $i<$oldSize; $i++) {
            if ($oldValues[$i] != $newValues[$i]) {
                return true;
            }
        }
        
        return false;
    }
    
    private function getValidSummands()
    {
        if ($this->validSummands === null) {
            $this->validSummands = [];
            $size = count($this->cells);
            $sums = self::$sums->getSummands($this->sum, $size);
            foreach ($sums as $summands) {
                $perms = Permutations::create($summands);
                foreach ($perms as $perm) {
                    array_push($this->validSummands, $perm);
                }
            }
        }
        
        return $this->validSummands;
    }

    private function getAllowedCellValues()
    {
        $valuesArr = array_map(function($cell) { return $cell->values; }, $this->cells);
        $validSummandsArr = $this->getValidSummands();
        
        $vsetArr = array_map([$this, 'arrayToSet'], $valuesArr);
        
        $sumsSetArr = [];
        $nSummands = count($this->cells);
        
        for ($i=0; $i<$nSummands; $i++) {
            array_push($sumsSetArr, new Set());
        }
     
        $this->validSummands = [];   
        foreach ($validSummandsArr as $valid) {
            if ($this->summandsValid($nSummands, $valid, $vsetArr)) {
                $this->addSummands($nSummands, $sumsSetArr, $valid);
                array_push($this->validSummands, $valid);
            }
        }
        
        return array_map([$this, 'setToArray'], $sumsSetArr);
    }
    
    private function summandsValid($n, $summands, $vsetArr)
    {
        for ($i=0; $i<$n; $i++) {
            $summand = $summands[$i];
            $vset = $vsetArr[$i];
            if (!$vset->contains($summand)) {
                return false;
            }
        }
        
        return true;
    }
    
    private function addSummands($n, &$sumsSetArr, $summands)
    {
        for ($i=0; $i<$n; $i++) {
            $sumsSet = &$sumsSetArr[$i];
            $sumsSet->add($summands[$i]);
        }
    }
    
    private function arrayToSet($arr)
    {
        return new Set($arr);
    }
    
    private function setToArray($set)
    {
        return $set->toArray();
    }
    
}

Group::classInit();